package is.ru.honn.rumblr.factory;

import is.ru.honn.rumblr.data.PostDetail;
import is.ru.honn.rumblr.domain.AbstractRumblrPost;
import is.ru.honn.rumblr.domain.RumblrPost;
import is.ru.honn.rumblr.domain.RumblrType;
import is.ru.honn.rumblr.util.RumblerLog;
import is.ru.honn.rumblr.util.XmlUtil;
import org.dom4j.Element;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;

abstract public class PostFactory
{
  abstract public RumblrPost createPostFromXML(Element e);
  abstract public List<PostDetail> createPostDetails(AbstractRumblrPost post);
  abstract public AbstractRumblrPost createPostType(String type);
  abstract public AbstractRumblrPost mergeDetailsWithPost(AbstractRumblrPost post, Collection<PostDetail> details);

  public static  AbstractRumblrPost mergeDetails(AbstractRumblrPost post, Collection<PostDetail> details) throws FactoryException
  {
       String type = post.getType(); //Save the type of the post into a string
       RumblrType typ = RumblrType.fromString(type);
       try
       {
           Class<? extends PostFactory> c = typ.getFactory();
           PostFactory factory = (PostFactory)c.newInstance();  //Creates the right kind of factory
           post = factory.mergeDetailsWithPost(post , details); //Calls a function to merge the details into the post
       }
       catch (Exception ex)
       {
            String msg = "Unable to create '" + type + "' while merging details";
            Logger.getLogger(RumblerLog.LOG_NAME).severe(msg);
            throw new FactoryException(ex);
       }
       return post;

  }
  //Creates the a post from the type sent in
  public static RumblrPost createPostFromType(String pType) throws FactoryException
  {

      RumblrType type = RumblrType.fromString(pType); //sets the right type
      RumblrPost post = null;
      try
      {
        Class<? extends PostFactory> c = type.getFactory();
        PostFactory factory = (PostFactory) c.newInstance();  //Creates the right kind of factory
        post = factory.createPostType(pType);  //Creates a post using the right factory type

      }
      catch (Exception ex)
      {
        String msg = "Unable to create '" + pType + "' while creating post from type";
        Logger.getLogger(RumblerLog.LOG_NAME).severe(msg);
        throw new FactoryException(ex);
      }

      return post;
  }


  public static List<PostDetail> createPostDetailsFromPost(AbstractRumblrPost post)
  {
      //check type, makes factory for type. In the TypeFactory there is a function that takes in
      // the AbstractRumblrPost and creates three fields (3 post details) and returns an array of them
      List<PostDetail> postDetails = new ArrayList<PostDetail>();
      RumblrType type = RumblrType.fromString(post.getType());
      try
      {
        Class<? extends PostFactory> c = type.getFactory();
        PostFactory factory = (PostFactory) c.newInstance();   //Creates the right kind of postFactory
        postDetails = factory.createPostDetails(post);  //Uses a factory function to populate the PostDetail collection
      }
      catch (Exception ex)
      {
        String msg = "Unable to create new type '" + post.getType() + "' while creating PostDetails";
        Logger.getLogger(RumblerLog.LOG_NAME).severe(msg);
      }

      return postDetails;
  }


  public static RumblrPost createPost(Element e) throws FactoryException
  {
    String xmlType = XmlUtil.getXPathValue(e, "@type");
    RumblrType type = RumblrType.fromString(xmlType);
    RumblrPost post = null;

    try
    {
      Class<? extends PostFactory> c = type.getFactory();
      PostFactory factory = (PostFactory) c.newInstance();

      post = factory.createPostFromXML(e);
    }
    catch (Exception ex)
    {
      String msg = "Unable to create new type '" + xmlType + "'";
      Logger.getLogger(RumblerLog.LOG_NAME).severe(msg);
      throw new FactoryException(ex);
    }
    return post;
  }

  public static PostFactory getFactory(String typename) throws FactoryException
  {
    RumblrType type = RumblrType.fromString(typename);
    PostFactory factory;
    try
    {
      Class<? extends PostFactory> c = type.getFactory();
      factory = (PostFactory)c.newInstance();
    }
    catch (Exception ex)
    {
      String msg = "Unable to create new type '" + typename + "'";
      Logger.getLogger(RumblerLog.LOG_NAME).severe(msg);
      throw new FactoryException(ex);
    }
    return factory;
  }

  public void createHeader(AbstractRumblrPost post, Element e)
  {
    post.setId(XmlUtil.getXPathValue(e, "@id"));
    post.setUrl(XmlUtil.getXPathValue(e, "@url"));
    post.setType(XmlUtil.getXPathValue(e, "@type"));
    try
    {
      post.setDateGmt(XmlUtil.getXPathValueAsDate(e, "@date-gmt"));
    }
    catch (ParseException e1)
    {
      post.setDateGmt(new Date());
    }

    post.addTags(XmlUtil.getMultipleValues(e, "tag"));
  }
}
